﻿using System;
using System.Diagnostics.Contracts;
using System.Reactive;

namespace Rxx.Parsers.Reactive
{
	/// <summary>
	/// Represents a parser over an observable sequence.
	/// </summary>
	/// <typeparam name="TSource">The type of the source elements.</typeparam>
	/// <typeparam name="TResult">The type of the elements that are generated from parsing the source elements.</typeparam>
	[ContractClass(typeof(IObservableParserContract<,>))]
#if !WINDOWS_PHONE
	public interface IObservableParser<TSource, out TResult>
#else
	public interface IObservableParser<TSource, TResult>
#endif
	{
		/// <summary>
		/// Gets a parser with a grammar that matches the next element in the source sequence.
		/// </summary>
		/// <remarks>
		/// A parser's grammar is defined in terms of grammar rules, each of which is defined in terms of the <see cref="Next"/> parser
		/// or another rule.
		/// </remarks>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Next",
			Justification = "Most appropriate term.")]
		IObservableParser<TSource, TSource> Next { get; }

		/// <summary>
		/// Applies the parser's grammar, which is defined in terms of the <see cref="Next"/> parser, to generate matches asynchronously.
		/// </summary>
		/// <param name="source">The observable sequence to parse.</param>
		/// <returns>An observable sequence of parse results that contain information about the matches.</returns>
		IObservable<IParseResult<TResult>> Parse(IObservableCursor<TSource> source);
	}

	[ContractClassFor(typeof(IObservableParser<,>))]
	internal abstract class IObservableParserContract<TSource, TResult> : IObservableParser<TSource, TResult>
	{
		public IObservableParser<TSource, TSource> Next
		{
			get
			{
				Contract.Ensures(Contract.Result<IObservableParser<TSource, TSource>>() != null);
				return null;
			}
		}

		public IObservable<IParseResult<TResult>> Parse(IObservableCursor<TSource> source)
		{
			Contract.Requires(source != null);
			Contract.Requires(source.IsForwardOnly);
			Contract.Ensures(Contract.Result<IObservable<IParseResult<TResult>>>() != null);
			return null;
		}
	}
}